﻿//-----------------------------------------------------------------------
// <copyright file="Executor.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the Executor class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine
{
    using ConflictResolution;
    using EngineOperation;
    using Tuples;
    using Tuple = Tuples.Tuple;

    /// <summary>
    /// Base class for generated executor classes.  The base class combines an instance of the engine, an agenda and
    /// a tuple store session, and therefore defines a production system.   This class implements the central logic 
    /// of the match-resolve-act cycle of a production system, including a deferred invocation mechansim for handling
    /// quantification.
    /// </summary>
    public abstract class Executor : Tulip.Engine.IExecutor
    {
        /// <summary>
        /// The current tuple store session.
        /// </summary>
        protected TupleStoreSession session;

        /// <summary>
        /// The engine instance being used by the executor.
        /// </summary>
        protected Engine engine;

        /// <summary>
        /// The agenda instance being used by the executor.
        /// </summary>
        protected Agenda agenda;

        /// <summary>
        /// Set of flags used to manage the adding of quantification monadic functions to the deferred
        /// execution invocation list.
        /// </summary>
        protected bool[] quantifications;

        ///////////// <summary>
        ///////////// Gets or sets the current tuple store session.
        ///////////// </summary>
        //////////protected TupleStoreSession Session
        //////////{
        //////////    get
        //////////    {
        //////////        return this.session;
        //////////    }

        //////////    set
        //////////    {
        //////////        this.session = value;
        //////////    }
        //////////}

        /// <summary>
        /// Asserts a fact to the executor.
        /// </summary>
        /// <param name="fact">The fact to be asserted.</param>
        public virtual void Execute(object fact)
        {
            var facts = new[] { fact };
            this.Execute(facts);
        }

        /// <summary>
        /// Asserts a fact to the executor.
        /// </summary>
        /// <param name="facts">Array of fact objects.</param>
        public virtual void Execute(object[] facts)
        {
            this.DoExecute(facts);

            // **********************
            // Retract Facts
            // **********************
            this.session.RetractAll();

            // Force a complete garbage collection here - we need to clear out any remining gen 2 stuff
            // GC.Collect(5, GCCollectionMode.Forced);
        }

        /// <summary>
        /// When overridden, increments the generation count.
        /// </summary>
        public abstract void IncrementGeneration();

        /// <summary>
        /// Performs execution of the rule set.  Continues through the match-resolve-act cycle
        /// until no further facts remain to be processed.
        /// </summary>
        /// <param name="facts">The initial facts to be processed by the engine.</param>
        internal void DoExecute(object[] facts)
        {
            while (facts.Length > 0)
            {
                // Assert the facts to the engine.
                var assertOperation = new AssertMany(this.session, facts);
                this.engine.AddAction(assertOperation);
                this.engine.Transition(ref facts);

                // Perform any deferred quantifications
                var quantifyOperation = new Quantify(this.session);
                this.engine.AddAction(quantifyOperation);
                this.engine.Transition(ref facts);

                // Clear the not flags
                for (var idx = 0; idx < this.quantifications.Length; idx++)
                {
                    this.quantifications[idx] = false;
                }

                // Increment the generation count
                this.IncrementGeneration();

                // Process agenda
                var fireRulesOperation = new FireRules(this.session, this.agenda);
                this.engine.AddAction(fireRulesOperation);
                this.engine.Transition(ref facts);
            }
        }

        /// <summary>
        /// Increments the generation count on each tuple in the tuple list.
        /// </summary>
        /// <param name="list">List of tuples.</param>
        protected static void DoIncrementGeneration(TupleLinkedList list)
        {
            Tuple tuple;
            while ((tuple = list.Next()) != null)
            {
                tuple.Generation = tuple.Generation + 1;
            }
        }
    }
}
